/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2007 Ben Motmans <ben.motmans@gmail.com>
 * Copyright (C) 2007 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections.Generic;

using Gtk;
using Glade;

using Anculus.Core;
using Anculus.Gui;

using Galaxium.Core;
using Galaxium.Protocol;
using Galaxium.Protocol.Gui;
using Galaxium.Gui;

namespace Galaxium.Gui.GtkGui
{
	public enum AccountWidgetPositions { Content, Progress };
	
	public abstract class AbstractAccountWidget : IAccountWidget<Widget>
	{
		public event EventHandler DisableChange;
		public event EventHandler EnableChange;
		
		public event EventHandler<SessionEventArgs> CreateSessionWidget;
		public event EventHandler<SessionEventArgs> CloseSessionWidget;
		public event EventHandler ShowLastSessionWidget;
		public event EventHandler<SessionEventArgs> ShowSessionWidget;
		
		public event EventHandler<AccountEventArgs> AccountChanged;
		
		[Widget("ContentBox")] VBox _content_box;
		[Widget("ProgressBox")] HBox _progress_box;
		
		protected IProtocol _protocol;
		protected IAccount _current_account;
		protected ISession _current_session;
		
		protected Widget _native_widget;
		protected Widget _layout_widget;
		protected Widget _parent_layout_widget;
		
		protected ComboBoxEntry _account_combo;
		protected Dictionary<string, int> _account_lookup = new Dictionary<string, int> ();
		
		protected IConfigurationSection _config;
		protected bool _connecting = false;
		
		public IAccount Account
		{
			get { return _current_account; }
			set
			{
				_current_account = value;
				OnAccountChanged (new AccountEventArgs (_current_account));
			}
		}
		
		public IProtocol Protocol
		{
			get { return _protocol; }
		}

		public Widget NativeWidget
		{
			get { return _native_widget; }
		}
		
		protected AbstractAccountWidget (IProtocol protocol)
		{
			_config = Configuration.Protocol.Section[protocol.Name];
			
			_protocol = protocol;
		}

		public virtual void Initialize ()
		{
			_native_widget = GladeUtility.ExtractWidget<Widget> (GladeUtility.GetGladeResourceStream (typeof (AbstractAccountWidget).Assembly, "AbstractAccountWidget.glade"), "Widget", this);
		}
		
		public virtual void Connect ()
		{
			_connecting = true;
		}
		
		public abstract void EnableFields ();
		public abstract void DisableFields (bool omit_cancel);
		
		public void ShowBox (AccountWidgetPositions position)
		{
			Box box = GetBox (position);
			box.Show ();
		}
		
		public void HideBox (AccountWidgetPositions position)
		{
			Box box = GetBox (position);
			box.Hide ();
		}
		
		public Gtk.Box GetBox (AccountWidgetPositions position)
		{
			switch (position)
			{
				case AccountWidgetPositions.Content:
					return _content_box;
				case AccountWidgetPositions.Progress:
					return _progress_box;
			}
			
			return null;
		}
		
		public void RemoveAccountWidget (AccountWidgetPositions position)
		{
			Box box = GetBox (position);
			box.Remove (box.Children[0]);
		}
		
		public void SetAccountWidget(AccountWidgetPositions position, Widget widget)
		{
			SetAccountWidget (position, widget, true, true, 0);
		}
		
		public virtual void SetAccountWidget (AccountWidgetPositions position, Widget widget, bool expand, bool fill, uint padding)
		{
			ThrowUtility.ThrowIfNull ("widget", widget);
			
			Box box = GetBox (position);
			box.PackStart (widget, expand, fill, padding);
			
			widget.ShowAll ();
		}
		
		protected virtual void PopulateFields ()
		{
			LoadAccounts ();
			
			SetLastAccount ();
			
			UpdateProgress (" ", 0.0);
			
			EnableFields();
		}
		
		public virtual void ApplyParentLayout (Widget parent)
		{
			ApplyParentLayout (parent.Allocation, _layout_widget.Allocation);

			_parent_layout_widget = parent;
			_parent_layout_widget.SizeAllocated += new SizeAllocatedHandler (ParentLayoutWidgetSizeAllocated);
		}
		
		protected virtual void LoadAccounts ()
		{
			foreach (IAccount account in AccountUtility.GetAccounts (_protocol))
				AddAccount (account);
		}
		
		protected virtual void AddAccount (IAccount account)
		{
			int index = _account_lookup.Count;
			
			_account_lookup.Add(account.UniqueIdentifier, index);
			_account_combo.InsertText (index, account.UniqueIdentifier);
		}
		
		protected virtual void RemoveAccount (IAccount account)
		{
			int index;
			
			if (_account_lookup.TryGetValue(account.UniqueIdentifier, out index))
			{
				_account_combo.RemoveText(index);
				_account_lookup.Remove(account.UniqueIdentifier);
				_account_lookup.GetEnumerator();
				
				int bigindex = 0;
				
				foreach (int val in _account_lookup.Values)
					if (val > bigindex)
						bigindex = val;
				
				for (int i = index; i <= bigindex; i++)
				{
					string keystring = String.Empty;
					
					if(_account_lookup.ContainsValue(i))
					{
						foreach(string key in _account_lookup.Keys)
							if (_account_lookup[key] >= i)
								keystring = key;
						
						_account_lookup[keystring]--;
					}
					else continue;
				}
			}
		}
		
		protected virtual void SetFirstAccount ()
		{
			TreeIter iter;
			
			if (_account_combo.Model.GetIterFirst (out iter))
				_account_combo.SetActiveIter(iter);
		}
		
		public virtual bool SelectAccount (string uid)
		{
			TreeIter iter;
			
			if (_account_combo.Model.GetIterFirst (out iter))
			{
				do {
					string t = (string)_account_combo.Model.GetValue (iter, 0);
					
					if (uid == t) {
						_account_combo.SetActiveIter (iter);
						return true;
					}
				} while (_account_combo.Model.IterNext (ref iter));
			}
			
			return false;
		}
		
		protected virtual void SetLastAccount ()
		{
			string previousAccountName = _config.GetString (Configuration.Protocol.LastAccount.Name, Configuration.Protocol.LastAccount.Default);
			
			SelectAccount (previousAccountName);
		}
		
		public virtual void Clear ()
		{
			SetLastAccount ();
			
			UpdateProgress (" ", 0.0);
			
			EnableFields ();
		}
		
		protected virtual ComboBoxEntry CreateAccountCombo ()
		{
			ComboBoxEntry cboAccount = ComboBoxEntry.NewText ();
			
			cboAccount.Entry.Changed += new EventHandler (AccountNameChanged);
			cboAccount.Entry.EditingDone += new EventHandler (AccountNameEditingDone);
			
			return cboAccount;
		}
		
		protected virtual ImageComboBox<IPresence> CreateStatusCombo (IPresence initial, params IPresence[] presences)
		{
			ImageComboBox<IPresence> cboStatus = new ImageComboBox<IPresence> (
				new ImageComboTextLookup<IPresence> (PresenceTextLookup),
				new ImageComboPixbufLookup<IPresence> (PresenceImageLookup)
			);
			
			foreach (IPresence presence in presences)
				cboStatus.Append (presence);
			
			cboStatus.Select (initial);
			cboStatus.Changed += new EventHandler (StatusComboChanged);
			
			return cboStatus;
		}
		
		protected virtual string PresenceTextLookup (IPresence item)
		{
			return item.State;
		}
		
		protected virtual Gdk.Pixbuf PresenceImageLookup (IPresence item)
		{
			return null;
		}
		
		protected virtual IAccount GetAccount (string name)
		{
			return AccountUtility.GetAccount (_protocol, name);
		}
		
		protected abstract IAccount SetAccount ();
		
		protected virtual void ParentLayoutWidgetSizeAllocated (object o, SizeAllocatedArgs args)
		{
			ApplyParentLayout (args.Allocation, _layout_widget.Allocation);
		}
		
		protected virtual void ApplyParentLayout (Gdk.Rectangle parentAlloc, Gdk.Rectangle alloc)
		{
			if (_parent_layout_widget == null)
				return;
			
			if (alloc.Width > parentAlloc.Width)
				_parent_layout_widget.SetSizeRequest (alloc.Width, _parent_layout_widget.HeightRequest);
			else if (alloc.Width < parentAlloc.Width)
				_layout_widget.SetSizeRequest (parentAlloc.Width, _layout_widget.HeightRequest);
		}
		
		protected virtual ISession CreateSession ()
		{
			IProtocolFactory factory = ProtocolUtility.GetProtocolFactory (_protocol);
			
			IAccount account = SetAccount ();
			
			if (!AccountUtility.AccountExists(_protocol, account.UniqueIdentifier))
				_account_combo.AppendText(account.UniqueIdentifier);
			
			AccountUtility.Save(account);
			
			_config.SetString (Configuration.Protocol.LastAccount.Name, _account_combo.Entry.Text);
			
			if (SessionUtility.SessionExists(account.UniqueIdentifier, account.Protocol))
			{
				// a session already exists with that account.
				if (ShowSessionWidget != null)
					ShowSessionWidget( this, new SessionEventArgs(SessionUtility.GetSession(account.UniqueIdentifier, account.Protocol)));
				
				return null;
			}
			
			SoundSetUtility.Play(Sound.Connecting);
			
			ISession session = factory.CreateSession (account) as ISession;
			session.LoginProgress += LoginProgressed;
			session.LoginCompleted += LoginCompleted;
			session.Disconnected += SessionDisconnected;
			session.ErrorOccurred += SessionErrorOccurred;

			return session;
		}
		
		protected virtual void RemoveSelectedAccount ()
		{
			if (AccountUtility.AccountExists(_protocol, _account_combo.Entry.Text))
			{
				IAccount account = AccountUtility.GetAccount(_protocol, _account_combo.Entry.Text);
				
				AccountUtility.RemoveAccount(account);
				AccountUtility.RemoveAccountFromConfig (account);
				
				RemoveAccount(account);
				
				SetFirstAccount ();
			}
		}
		
		protected virtual void LoginProgressed (object sender, SessionProgressEventArgs args)
		{
			UpdateProgress (args.Message, args.Percent);
		}
		
		protected virtual void LoginCompleted (object sender, SessionEventArgs args)
		{
			if (CreateSessionWidget != null)
				CreateSessionWidget (this, args);
			
			_connecting = false;
			
			SoundSetUtility.Play(Sound.Connected);
		}
		
		protected virtual void SessionDisconnected (object sender, SessionEventArgs args)
		{
			UpdateProgress(" ", 0.0);
			
			args.Session.LoginProgress -= LoginProgressed;
			args.Session.LoginCompleted -= LoginCompleted;
			args.Session.Disconnected -= SessionDisconnected;
			args.Session.ErrorOccurred -= SessionErrorOccurred;
			
			OnCloseSessionWidget (args);
			
			SoundSetUtility.Play(Sound.Disconnected);
		}

		protected virtual void SessionErrorOccurred (object sender, ErrorEventArgs args)
		{
			UpdateProgress(" ", 0.0);
			
			ErrorOccurred (args.Description);
			
			args.Session.LoginProgress -= LoginProgressed;
			args.Session.LoginCompleted -= LoginCompleted;
			args.Session.Disconnected -= SessionDisconnected;
			args.Session.ErrorOccurred -= SessionErrorOccurred;
			
			OnCloseSessionWidget (args);
			
			SoundSetUtility.Play(Sound.Error);
		}
		
		protected abstract void ErrorOccurred (string message);
		
		protected abstract void UpdateProgress (string message, double percent);
		
		protected virtual void CancelLogin ()
		{
			if (ShowLastSessionWidget != null)
				ShowLastSessionWidget (this, EventArgs.Empty);
		}
		
		protected virtual void StatusComboChanged (object sender, EventArgs args)
		{
		}
		
		protected virtual void AccountNameEditingDone (object sender, EventArgs args)
		{
		}
		
		protected abstract void AccountNameChanged (object sender, EventArgs args);
		
		protected virtual void OnCloseSessionWidget (SessionEventArgs args)
		{
			if (CloseSessionWidget != null)
				CloseSessionWidget (this, args);
		}
		
		protected virtual void OnDisableChange (EventArgs args)
		{
			if (DisableChange != null)
				DisableChange (this, args);
		}
		
		protected virtual void OnEnableChange (EventArgs args)
		{
			if (EnableChange != null)
				EnableChange (this, args);
		}
		
		protected virtual void OnAccountChanged (AccountEventArgs args)
		{
			if (AccountChanged != null)
				AccountChanged (this, args);
		}
	}
}